package framework.needscan.inject;

import framework.annotation.Inject;
import framework.scanner.BaseScanner;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author 马洁
 * @date 2020/3/12 - 11:16
 */
public class InjectScanner extends BaseScanner {
    private static final int DEFAULT_SIZE = 16;
    private static Map<Class<?>, Class<?>> classMap;
    private static InjectScanner injectScanner = new InjectScanner();

    private InjectScanner() {
        classMap = new HashMap<>(DEFAULT_SIZE);
    }

    public static InjectScanner getInstance() {
        return injectScanner;
    }

    public static void add(Class<?> originCls, Class<?> realCls) {
        classMap.put(originCls, realCls);
    }

    @Override
    public void scan(Class<?> cls) {
        try {
            injectField(cls);
        } catch (IllegalAccessException e) {
            System.out.println("非法操作");
            e.printStackTrace();
        }
    }

    private void injectField(Class<?> cls) throws IllegalAccessException {
        Object classObject = getObjectPool().getClassInstance(cls);

        for (Field field : cls.getDeclaredFields()) {
            field.setAccessible(true);

            if (Objects.nonNull(getInjectAnnotation(field))) {
                field.set(classObject,
                        getObjectPool().getClassInstance(getNeedInjectClass(field)));
            }
        }
    }

    private Class<?> getNeedInjectClass(Field field) {
        Class<?> defaultInjectClassValue = getInjectAnnotation(field).value();

        Class<?> finalInjectClassValue = Objects.equals(Void.class, defaultInjectClassValue) ?
                field.getType() :
                defaultInjectClassValue;

        return classMap.getOrDefault(finalInjectClassValue, finalInjectClassValue);
        /*
         * 相当于：
         * return classMap.containsKey(injectClassValue) ?
                classMap.get(injectClassValue) :
                injectClassValue;
         */
    }

    private Inject getInjectAnnotation(Field field) {
        return field.getAnnotation(Inject.class);
    }
}
